"""
线性回归
明白各个回归算法的优劣
明白如何衡量回归算法的效果
了解什么时间该选用什么回归

Sklearn类
普通线性回归LinearRegression
linear_model.LinearRegression 使用普通最小二乘法的线性回归

岭回归Ridge
linear_model.Ridge              岭回归，一种将L2作为正则化工具的线性最小二乘回归
linear_model.RidgeCV            带交叉验证的岭回归
linear_model.RidgeClassifier    岭回归的分类器
linear_model.RidgeClassifierCV  带交叉验证的岭回归的分类器
linear_model.ridge_regression   【函数】用正太方程法求解岭回归

LASSO
linear_model.Lasso              Lasso，使用L1作为正则化工具来训练的线性回归模型
linear_model.LassoCV            带交叉验证和正则化迭代路径的Lasso
linear_model.LassoLars          使用最小角度回归求解的Lasso
linear_model.LassoLarsCV        带交叉验证的使用最小角度回归求解的Lasso
linear_model.LassoLarsIC        使用BIC或AIC进行模型选择的，使用最小角度回归求解的Lasso
linear_model.MultiTaskLasso     使用L1 / L2混合范数作为正则化工具训练的多标签Lasso
linear_model.MultiTaskLassoCV   使用L1 / L2混合范数作为正则化工具训练的，带交叉验证的多标签Lasso
linear_model.lasso_path         【函数】用坐标下降计算Lasso路径

弹性网
linear_model.ElasticNet         弹性网，一种将L1和L2组合作为正则化工具的线性回归
linear_model.ElasticNetCV       带交叉验证和正则化迭代路径的弹性网
linear_model.MultiTaskElasticNet    多标签弹性网
linear_model.MultiTaskElasticNetCV  带交叉验证的多标签弹性网
linear_model.enet_path          【函数】用坐标下降法计算弹性网的路径

方法
最小角度回归
linear_model.Lars               最小角度回归（Least Angle Regression，LAR）
linear_model.LarsCV             带交叉验证的最小角度回归模型
linear_model.lars_path          【函数】使用LARS算法计算最小角度回归路径或Lasso的路径

正交匹配追踪
linear_model.OrthogonalMatchingPursuit 正交匹配追踪模型（OMP）
linear_model.OrthogonalMatchingPursuitCV 交叉验证的正交匹配追踪模型（OMP）
linear_model.orthogonal_mp 【函数】正交匹配追踪（OMP）
linear_model.orthogonal_mp_gram 【函数】Gram正交匹配追踪（OMP）

贝叶斯回归
linear_model.ARDRegression  贝叶斯ARD回归。ARD是自动相关性确定回归（Automatic Relevance Determination Regression），是一种类似于最小二乘的，用来计算参数向量的数学方法。
linear_model.BayesianRidge  贝叶斯岭回归

其他回归
linear_model.PassiveAggressiveClassifier 被动攻击性分类器
linear_model.PassiveAggressiveRegressor 被动攻击性回归
linear_model.Perceptron 感知机
linear_model.RANSACRegressor RANSAC（RANdom SAmple Consensus）算法。
linear_model.HuberRegressor 胡博回归，对异常值具有鲁棒性的一种线性回归模型
linear_model.SGDRegressor 通过最小化SGD的正则化损失函数来拟合线性模型
linear_model.TheilSenRegressor Theil-Sen估计器，一种鲁棒的多元回归模型

RSS残差平方和
通过最小化真实值和预测值之间的RSS来求解参数的方法叫做最小二乘法

"""

"""
参数 含义
fit_intercept   布尔值，可不填，默认为True。是否计算此模型的截距。如果设置为False，则不会计算截距。
normalize       布尔值，可不填，默认为False。当fit_intercept设置为False时，将忽略此参数。如果为True，则特征矩阵X在进入回归之前将会被减去均值（中心化）并除以L2范式（缩放）。如果你希望进行标准化，请在fit数据之前使用preprocessing模块中的标准化专用类StandardScaler。
copy_X          b布尔值，可不填，默认为True。如果为真，将在X.copy()上进行操作，否则的话原本的特征矩阵X可能被线性回归影响并覆盖。
n_jobs          整数或者None，可不填，默认为None。用于计算的作业数。只在多标签的回归和数据量足够大的时候才生效。除非None在joblib.parallel_backend上下文中，否则None统一表示为1。如果输入 -1，则表示使用全部的CPU来进行计算。

线性回归的性能，往往取决于数据本身，而并非是我们的调参能力，线性回归也因此对数据有着很高的要求。
"""
from sklearn.linear_model import LinearRegression as LR
from sklearn.model_selection import train_test_split
from sklearn.model_selection import cross_val_score
from sklearn.datasets import fetch_california_housing as fch #加利福尼亚房屋价值数据集
import pandas as pd

housevalue = fch() #会需要下载，大家可以提前运行试试看

X = pd.DataFrame(housevalue.data) #放入DataFrame中便于查看
y = housevalue.target
X.columns = housevalue.feature_names

Xtrain, Xtest, Ytrain, Ytest = train_test_split(X,y,test_size=0.3,random_state=420)
reg = LR().fit(Xtrain, Ytrain)
yhat = reg.predict(Xtest)
# print(reg.coef_)        # 线性回归方程中估计出的系数
print([*zip(Xtrain.columns,reg.coef_)])     # 每个feature_name对应的系数
print("intercept: ",reg.intercept_)   # 线性回归中的截距项

"""
回归类算法的模型评估一直都是回归算法中的一个难点，但不像我们曾经讲过的无监督学习算法中的轮廓系数等等评估指标，回归类与分类型算法的模型评估其实是相似的法则——找真实标签和预测值的差异。
只不过在分类型算法中，这个差异只有一种角度来评判，那就是是否预测到了正确的分类，而在我们的回归类算法中，我们有两种不同的角度来看待回归的效果：
第一，我们是否预测到了正确的数值。   MSE均方误差/MAE绝对均值误差
第二，我们是否拟合到了足够的信息。   R2可解释性方差分数
这两种角度，分别对应着不同的模型评估指标
"""

from sklearn.metrics import mean_squared_error as MSE
from sklearn.metrics import mean_absolute_error as MAE
print("MSE:%f" % MSE(yhat,Ytest))
# print("MAE:%f" % MAE(yhat,Ytest))
print("max:%f, min:%f" %(y.max(),y.min()))
"""
MSE和Y的MEAN/MAX/MIN比较，如果MES在MAX和MIN之间，没有超过MIN太多，则说明预测效果不错。
本例中，MSE/MIN超过300%，说明预测效果一般。
"""

# 交叉验证10次的MSE/MAE
# cross_val_score会返回一个包含每次验证的scoring的List
print("cross_val_score_mean: ",cross_val_score(reg,X,y,cv=10,scoring="neg_mean_squared_error").mean())  # MSE
# print(cross_val_score(reg,X,y,cv=10,scoring="neg_mean_absolute_error").mean()) # MAE
"""
sklearn在计算模型评估指标的时候，会考虑指标本身的性质，均方误差本身是一种误差，所以被sklearn划分为模型的一种损失(loss)。
在sklearn当中，所有的损失都使用负数表示，因此均方误差也被显示为负数了。
真正的均方误差MSE的数值，其实就是neg_mean_squared_error去掉负号的数字。
"""

"""
我们希望找到新的指标，除了判断预测的数值是否正确之外，还能够判断我们的模型是否拟合了足够多的，数值之外的信息。

我们使用方差来衡量数据上的信息量。如果方差越大，代表数据上的信息量越多，而这个信息量不仅包括了数值的大小，还包括了我们希望模型捕捉的那些规律。
为了衡量模型对数据上的信息量的捕捉，我们定义了R2和可解释性方差分数(explained_variance_score，EVS)
R2代表留存的信息量占比。越大说明模型损失的信息越小，拟合越好。
"""

r2 = reg.score(Xtest,Ytest)
print("R2: ",r2)

from sklearn.metrics import r2_score
# 使用r2_score要注意参数的顺序，先真实值，后预测值
print("R2: ",r2_score(Ytest,      # y_true
                      yhat    # y_pred
                      ))
#或者你也可以指定参数，就不必在意顺序了
print(r2_score(y_true = Ytest,y_pred = yhat))

# 交叉验证R2
print(cross_val_score(reg,X,y,cv=10,scoring="r2").mean())

"""
EVS和R2是异曲同工的，两者都是衡量 1 - 没有捕获到的信息占总信息的比例
两者虽然相似，但却并不完全相等
"""
#调用EVS
from sklearn.metrics import explained_variance_score as EVS
print("EVS: ",EVS(Ytest,yhat))
print("cross_val_score_EVS: ",cross_val_score(reg,X,y,cv=10,scoring="explained_variance"))


import matplotlib.pyplot as plt
sorted(Ytest)
plt.plot(range(len(Ytest)),sorted(Ytest),c="black",label= "Data")
plt.plot(range(len(yhat)),sorted(yhat),c="red",label = "Predict")
plt.legend()
plt.show()


import numpy as np
rng = np.random.RandomState(42)
X = rng.randn(100, 80)
y = rng.randn(100)
print(cross_val_score(LR(), X, y, cv=5, scoring='r2'))
"""
可以观察到，交叉验证的R2都是负的。
当我们的 显示为负的时候，这证明我们的模型对我们的数据的拟合非常糟糕，模型完全不能使用。
现实应用中，如果你发现你的线性回归模型出现了负的R2，不代表你就要接受他了。
首先检查你的建模过程和数据处理过程是否正确，也许你已经伤害了数据本身，也许你的建模过程是存在bug的。
如果你检查了所有的代码，也确定了你的预处理没有问题，但你的R2也还是负的，那这就证明，线性回归模型不适合你的数据，试试看其他的算法吧。
"""


